package ga;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class Algorithm {

    public Algorithm(int carsNumber, Graph graph, int populationSize) {
        this.carsNumber = carsNumber;
        this.graph = graph;
        this.populationSize = populationSize;
    }
    private Population p = new Population();

    public Population createPopulation() {
        p = new Population(populationSize);
        for (int i = 0; i < populationSize; i++) {
            Individuum a = createIndividuum();
            p.addIndividuum(a);
            //System.out.println("Average: " + a.getRes());
        }
        return p;
    }

    public Individuum createIndividuum() {
        Individuum ind = new Individuum(graph);
        for (int i = 0; i < carsNumber; i++) {
            Path p = graph.findRandomPath();
            ind.addPath(p);
        }
        BigDecimal res = ind.countAverageTime();
        System.out.println(ind.toString());
        return ind;
    }

    //1. selection
    public void selection() {

        Collections.sort(p.getPopulation(), new IndividuumComparartor());

//        System.out.println("***selection***");
//        for (int i = 0; i < p.getPopulation().size(); i++) {
//            System.out.println(p.getPopulation().get(i).getAverageTime() + "  " + p.getPopulation().get(i));
//        }
//        int t = populationSize/2;
//        
//        List<Individuum> pop = p.getPopulation().subList(0, t);
//        p.setPopulation(pop);
    }

    public void start(int b) {
        for(int r = 0; r < b; r++){
            System.out.println(p.toString());
            //selection();
            Random rand = new Random();
            // crossover
            while (p.getPopulation().size() < populationSize) {
                int n1 = rand.nextInt(populationSize);
                int n2 = rand.nextInt(populationSize);
                System.out.println(p.getPopulation().get(0).getRes());
                //System.out.println("n1: " + n1 + " n2: " + n2);
                Individuum ind1 = p.getPopulation().get(n1);
                Individuum ind2 = p.getPopulation().get(n2);
                List<Individuum> result = crossover(ind1, ind2);
                p.getPopulation().add(ind2);
                p.getPopulation().add(ind1);
               // System.out.println("before crossover: \n" + ind1.toString() + ind2.toString());
               // System.out.println("after crossover: \n" + result.get(0).toString() + result.get(1).toString());
            }
            // mutation
            int t = populationSize/4;
            for(int i = 0; i < t; i ++){
                mutation();
            }

            System.out.println(p.toString());
            selection();
            System.out.println("result: " + p.getPopulation().get(0).getRes());
        }
    }

    private List<Individuum> crossover(Individuum ind1, Individuum ind2) {
        Random rand = new Random();
        int crossoverPoint = 1 + rand.nextInt(carsNumber - 2);// [1...carsNumber-1]
        //System.out.println("***crossover***");
        //System.out.println("crossover point: " + crossoverPoint);
        List<Path> l1 = new ArrayList<Path>();
        List<Path> l2 = new ArrayList<Path>();
        for (int i = 0; i < carsNumber; i++) {
            if (i < crossoverPoint) {
                l1.add(ind1.getPaths().get(i));
                l2.add(ind2.getPaths().get(i));
            }
            if (i >= crossoverPoint) {
                l1.add(ind2.getPaths().get(i));
                l2.add(ind1.getPaths().get(i));
            }
        }
        List<Individuum> result = new ArrayList<Individuum>();
        result.add(new Individuum(l1));
        result.add(new Individuum(l2));
        return result;
    }

    public void mutation() {
        //System.out.println("***mutation***");
        Random rand = new Random();
        int indNumForMutation = rand.nextInt(populationSize);
        int indSize = p.getIndividuumAt(indNumForMutation).getPaths().size();
        int mutationPoint = rand.nextInt(indSize);
        Path temp = graph.findRandomPath();
        p.getPopulation().get(indNumForMutation).getPaths().set(mutationPoint, temp);
    }
    
    private int carsNumber;
    private Graph graph;
    private Timetable timetable;
    private int populationSize;
}
